package consumption;

import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import Configuration.Clock;


/**
 * Stores all the voltage values and measures the consumption. Also checks for outage
 * 
 * @author Pavel
 *
 */
public class ConsumptionMeasurement extends Thread{
	
	private int ampereValue;
	private double hours;
	//stores all the voltage data from PinReader. The String is a time-stamp
	//and the Double is the voltage read from the pin
	private ConcurrentHashMap<String, Double> voltageData;
	private Sensor sensor;
	private boolean powerOn;
	private boolean outage;
	
	public ConsumptionMeasurement(int ampValue, double hours){
		this.ampereValue = ampValue;
		this.hours = hours;
		this.sensor = new Sensor();
		this.voltageData = new ConcurrentHashMap<String,Double>();
		this.powerOn = true;
		this.outage = false;
	}
	
	public void setAmpValue(int ampValue){
		this.ampereValue = ampValue;
	}
	
	public void setHours (double hours){
		this.hours = hours;
	}
	
	public void setPowerOn(boolean power){
		this.powerOn = power;
	}
	
	/**
	 * infinite while loop measuring the data
	 */
	public void run() {
        while(true){
        	
        	int count = 0;
        	
        	if(powerOn){
	        	double usage = this.measureVoltage();
	        	//check if read voltage value is 0
	        	count = (usage == 0) ? count++ : 0;
	        	
	        	//if 30 consecutive 0 values, assume a power outage
	        	if(count >= 30){
	        		this.outage = true;
	        	}
	        
				Timestamp timestamp = new Timestamp(new Date().getTime());
				String s = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss").format(timestamp);
				voltageData.put(s, usage);
				//System.out.println("At " + s + "consumption is: " + usage);
				
	        	try {
					Clock.pause();
				} catch (InterruptedException e) {
				
					e.printStackTrace();
				}
        	}
        	else {
        		try {
        			System.out.println("[SmartMeter] Power Off");
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
        	}
        }
        
    }
	
	/**
	 * measures the voltage from the Sensor
	 * 
	 * @return
	 */
	private Double measureVoltage(){
		return (Double) sensor.getVoltage();
	}
	
	/**
	 * Helper method, returns a timestamp from a string
	 * @param time
	 * @return
	 * @throws ParseException
	 */
	private Timestamp getTimestamp(String time) throws ParseException{
		
		SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm");
		Date parsedDate = dateFormat.parse(time);
		Timestamp timeStamp = new Timestamp(parsedDate.getTime());
		return timeStamp;
	}
	
	/**
	 * Returns the consumption data by reading in voltage values from the HashMap
	 * and calculating the RMS voltage and finally the consumption in Kilo-Watt Hour 
	 * 
	 * @param timeStamp
	 * @return
	 * @throws ParseException 
	 */
	public synchronized double getConsumption(String timeStamp) throws ParseException{
		
		Timestamp requestedTimestamp = getTimestamp(timeStamp.replaceAll("$", ""));
		Vector<Double> voltages = new Vector<Double>();
		//System.out.println("Size of consumption data: " + consumptionData.size());
		//iterate the whole map
		for(String s: voltageData.keySet()){
			
			Timestamp mStamp =  getTimestamp(s.substring(0,s.length() - 3));
			//if the stored timestamp occurred before the requested timestamp
			if(mStamp.before(requestedTimestamp)){
				
				long timeDiff = requestedTimestamp.getTime() - mStamp.getTime();
				timeDiff = timeDiff/(60 * 1000);
				
				//if difference is less than or equal to 1 minute
				if(timeDiff <= 1){
			
					voltages.add(voltageData.get(s));
				}
				
			}
			
		}
		//System.out.println("Vector size: " + voltages.size());
		return getKWH(calculateRMSVoltage(voltages));
	}
	
	/**
	 * Calculates the RMS voltage
	 * @param voltages
	 * @return rms voltage 
	 */
	private double calculateRMSVoltage(Vector<Double> voltages){
        
		double rmsVoltage = 0;
        for (int i = 0; i < voltages.size(); i++)
        	rmsVoltage += voltages.get(i) * voltages.get(i);
        rmsVoltage /= voltages.size();
        return Math.sqrt(rmsVoltage);
	}
	
	/**
	 * Returns the consumption in Kilo-Watt Hour
	 * @param rmsVoltage
	 * @return 
	 */
	private double getKWH(double rmsVoltage){
		try{
		DecimalFormat df = new DecimalFormat("#.####");
		
		double power = rmsVoltage * this.ampereValue;
		return Double.parseDouble(df.format(power * this.hours /1000)) ;
		
		} catch (Exception e){
			return -1;
		}
		
	}
	
	/**
	 * Returns the whole map
	 * @return 
	 */
	public Map<String,Double> getAllConsumptionData(){
		return voltageData;
	}
	
	/**
	 *Returns the status of the outage
	 * @return
	 */
	public boolean getOutage(){
		return this.outage;
	}
}
